package base.jdk8_demo.lambda.demo07;

import base.jdk8_demo.lambda.demo02.Emp;
import base.jdk8_demo.lambda.demo02.Status;
import org.junit.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.DoubleStream;

/**
 * Map - Reduce 是大数据中用到的模式
 * 规约：
 * reduce(T identity, BinaryOperator) / reduce(BinaryOperator) -可以将流中元素反复结合起来，得到一个值。
 * 收集：
 * collect - 将流转换为其他形式。接收一个java.util.stream.Collector接口的实现，用于给Stream中元素做汇总的方法。
 */
public class TestStream3 {
    List<Emp> emps1 = Arrays.asList(
            new Emp("zs",16,1000.0, Status.VACATION),
            new Emp("ls", 18, 3000.0, Status.FREE),
            new Emp("ww", 17, 2000.0, Status.VACATION),
            new Emp("zl", 19, 4000.0, Status.FREE),
            new Emp("dg", 20, 5000.0, Status.BUSY),
            new Emp("tom", 21, 6000.0, Status.BUSY)
    );
    List<Emp>  emps2 = Arrays.asList(
            new Emp("zs",16,1000.0, Status.VACATION),
            new Emp("ls", 18, 3000.0, Status.FREE),
            new Emp("ww", 17, 2000.0, Status.VACATION),
            new Emp("ww", 17, 2000.0, Status.BUSY)
        );
    /**
     * 计算一批数的累加之和
     */
    @Test
    public void reduceTest1(){
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        /*
         	先赋值0->x,1->y,计算x+y结果并赋值给x,2->y,再进行+运算结果赋值给x，以此类推,直至list遍历结束。
         */
        Integer reduce = list.stream()
                .reduce(0, (x, y) -> x + y);
//		        .reduce((x, y) -> x + y);

        System.out.println("1-10之和："+reduce);

        Optional<Double> op = emps1.stream()
                .map(Emp::getSalary)
                .reduce((x,y)-> x+y);
//                .reduce(Double::sum);
        System.out.println("人员工资总和："+op.get());
    }

    /**
     * 提取员工姓名，并封装到不同的集合中
     */
    @Test
    public void collectTest(){
        System.out.println("----------------转换为List-----------------");
//        Stream<Emp> empStream = emps2.stream();
//        Stream<String> stringStream = empStream.map(Emp::getName);
//        List<String> list = stringStream.collect(Collectors.toList());
        List<String> list = emps2.stream()
                .map(Emp::getName)
                .collect(Collectors.toList());
        System.out.println(list);
        list.forEach(System.out::println);

        System.out.println("----------------转换为Set-----------------");
        Set<String> set = emps2.stream()
                .map(Emp::getName)
                .collect(Collectors.toSet());
        set.forEach(System.out::println);

        System.out.println("----------------转换为HashSet-----------------");
        HashSet<String> hashSet = emps2.stream()
                .map(Emp::getName)
                .collect(Collectors.toCollection(HashSet::new));
        hashSet.forEach(System.out::println);
    }

    /**
     * 常规的统计聚合函数使用
     */
    @Test
    public void normalTest(){
        //1、总数：总人数统计
        Long count = emps2.stream()
                .collect(Collectors.counting());
//      count = emps2.stream().count();//推荐
        System.out.println("员工总人数："+count);
        //2、平均值：平均工资
        Double avg = emps2.stream()
                .collect(Collectors.averagingDouble(Emp::getSalary));
        System.out.println("平均工资为："+avg);
        //3、总和：工资总和
        Double total = emps2.stream()
                .collect(Collectors.summingDouble(Emp::getSalary));
//        total = emps2.stream().mapToDouble(Emp::getSalary).sum();//推荐
        System.out.println("工资总和："+total);
        //4、最大值：工资最高的员工
        Optional<Emp> max = emps2.stream()
                .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));
//        max = emps2.stream().max((e1, e2) -> e1.getSalary().compareTo(e2.getSalary()));//推荐
        System.out.println("工资最高的员工是："+max.get());
        
        max = emps2.stream()
//        	 .sorted((e1,e2) -> e2.getSalary().compareTo(e1.getSalary()))
                .sorted((e1,e2) -> Double.compare(e2.getSalary(),e1.getSalary()))
        	 .findFirst();
        System.out.println("工资最高的员工是："+max);
        //5、最小值：员工最低工资
        Optional<Double> min = emps2.stream()
                .map(Emp::getSalary) // 转换成Stream<Double>流
                .collect(Collectors.minBy((d1, d2) -> Double.compare(d1, d2)));
//                .collect(Collectors.minBy(Double::compare));
        min = emps2.stream()
                .map(Emp::getSalary)
                .min(Double::compare);
        System.out.println("员工最低工资："+min.get());
    }

    /**
     * 分组
     */
    @Test
    public void groupTest(){
        // 按照员工的 Status 分组
        Map<Status, List<Emp>> map = emps1.stream()
                .collect(Collectors.groupingBy(Emp::getStatus));
        System.out.println(map);
        /*
         {
            FREE=[
                Emp(id=null, name=ls, age=18, salary=3000.0, status=FREE),
                Emp(id=null, name=zl, age=19, salary=4000.0, status=FREE)
                ],
            BUSY=[
                Emp(id=null, name=dg, age=20, salary=5000.0, status=BUSY),
                Emp(id=null, name=tom, age=21, salary=6000.0, status=BUSY)
                ],
            VOCATON=[
                Emp(id=null, name=zs, age=16, salary=1000.0, status=VACATION),
                Emp(id=null, name=ww, age=17, salary=2000.0, status=VACATION)
                ]
          }
         */
    }

    /**
     * 多级分组
     * 本测试案例演示了两级分组：先按Status，再按Age分组
     */
    @Test
    public void multipleGroupTest(){
        emps1 = Arrays.asList(
                new Emp("zs",16,1000.0, Status.VACATION),
                new Emp("ls", 38, 3000.0, Status.FREE),
                new Emp("ww", 17, 2000.0, Status.VACATION),
                new Emp("zl", 39, 4000.0, Status.FREE),
                new Emp("dg", 60, 5000.0, Status.BUSY),
                new Emp("tom", 61, 6000.0, Status.BUSY)
        );
        Map<Status, Map<String, List<Emp>>> mmap = emps1.stream()
                .collect(Collectors.groupingBy(Emp::getStatus, Collectors.groupingBy(emp -> {
                    if (emp.getAge() <= 35) {
                        return "青年";
                    } else if (emp.getAge() <= 50) {
                        return "中年";
                    } else {
                        return "老年";
                    }
                })));
        System.out.println(mmap);
        /*
        {
            VOCATON={
                青年=[
                    Emp(id=null, name=zs, age=16, salary=1000.0, status=VACATION),
                    Emp(id=null, name=ww, age=17, salary=2000.0, status=VACATION)
                 ]
             },
             FREE={
                中年=[
                    Emp(id=null, name=ls, age=38, salary=3000.0, status=FREE),
                    Emp(id=null, name=zl, age=39, salary=4000.0, status=FREE)
                 ]
             },
             BUSY={
                老年=[
                    Emp(id=null, name=dg, age=60, salary=5000.0, status=BUSY),
                    Emp(id=null, name=tom, age=61, salary=6000.0, status=BUSY)
                 ]
              }
          }
         */
    }

    /**
     * 分区： 满足条件的分配到true区，和不满足条件的分配到false区
     * 本测试用例 按照工资分成 大于5000的和小于5000两个区
     */
    @Test
    public void partitionTest(){
        Map<Boolean, List<Emp>> map = emps1.stream()
                .collect(Collectors.partitioningBy(emp -> emp.getSalary() > 5000));
        System.out.println(map);
        /*
        {
            false=[
                Emp(id=null, name=zs, age=16, salary=1000.0, status=VACATION),
                Emp(id=null, name=ls, age=18, salary=3000.0, status=FREE),
                Emp(id=null, name=ww, age=17, salary=2000.0, status=VACATION),
                Emp(id=null, name=zl, age=19, salary=4000.0, status=FREE),
                Emp(id=null, name=dg, age=20, salary=5000.0, status=BUSY)
                ],
            true=[
                Emp(id=null, name=tom, age=21, salary=6000.0, status=BUSY)
                ]
         }
         */
    }

    /**
     * 统计的另一种获取方式
     */
    @Test
    public void otherTest(){
        DoubleSummaryStatistics dss = emps2.stream()
                .collect(Collectors.summarizingDouble(Emp::getSalary));

        System.out.println("员工总数："+dss.getCount());
        System.out.println("工资总额："+dss.getSum());
        System.out.println("平均工资额："+dss.getAverage());
        System.out.println("最高工资："+dss.getMax());
        System.out.println("最低工资："+dss.getMin());
    }

    /**
     * 提取并重新组装
     */
    @Test
    public void joinTest(){
        String str = emps2.stream()
                .map(Emp::getName)
                .collect(Collectors.joining(",", "[", "]"));
        System.out.println(str);//[zs,ls,ww,ww]
    }

}
